using System;
using System.IO;
using Microsoft.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WolvenKit.Core.Extensions;

namespace WolvenKit.UnitTests
{
    [TestClass]
    public class BinaryWriterExtensionTests
    {
        #region Helpers

        private static readonly RecyclableMemoryStreamManager s_streamManager = new();

        #endregion

        #region Methods


        [DataTestMethod]
        [DataRow("", new byte[] { 0x00 }, DisplayName = "00. Empty string")]
        [DataRow("Reset", new byte[] { 0x85, 0x52, 0x65, 0x73, 0x65, 0x74 }, DisplayName = "01. From tweakdb.bin: Reset")]
        [DataRow(
            "NPCType.Drone",
            new byte[] { 0x8D, 0x4E, 0x50, 0x43, 0x54, 0x79, 0x70, 0x65, 0x2E, 0x44, 0x72, 0x6F, 0x6E, 0x65 },
            DisplayName = "02. From tweakdb.str: 'Corporate'"
        )]
        [DataRow(
            "Suichū Yōgan syn-fiber combat shirt",
            new byte[] {
                0xA5, 0x53, 0x75, 0x69, 0x63, 0x68, 0xC5, 0xAB, 0x20, 0x59, 0xC5, 0x8D, 0x67, 0x61, 0x6E, 0x20,
                0x73, 0x79, 0x6E, 0x2D, 0x66, 0x69, 0x62, 0x65, 0x72, 0x20, 0x63, 0x6F, 0x6D, 0x62, 0x61, 0x74,
                0x20, 0x73, 0x68, 0x69, 0x72, 0x74
            },
            DisplayName = "03. From lang_en_text/onscreens.json: Suichū Yōgan syn-fiber combat shirt"
        )]
        [DataRow(
            "パナムの車のキーを取り返す",
            new byte[] {
                0xA7, 0xE3, 0x83, 0x91, 0xE3, 0x83, 0x8A, 0xE3, 0x83, 0xA0, 0xE3, 0x81, 0xAE, 0xE8, 0xBB, 0x8A,
                0xE3, 0x81, 0xAE, 0xE3, 0x82, 0xAD, 0xE3, 0x83, 0xBC, 0xE3, 0x82, 0x92, 0xE5, 0x8F, 0x96, 0xE3,
                0x82, 0x8A, 0xE8, 0xBF, 0x94, 0xE3, 0x81, 0x99
            },
            DisplayName = "04. From lang_jp_text/onscreens.json: パナムの車のキーを取り返す"
        )]
        [DataRow(
            "Roaming the Badlands, looting scrapyards, raiding fuel depots – life on the road wasn't easy. But growing up in a nomad clan has its perks. Honesty, integrity, and a love of freedom – qualities that few in Night City possess, and no amount of money can buy.",
            new byte[] {
                0xC5, 0x04, 0x52, 0x6F, 0x61, 0x6D, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x42, 0x61,
                0x64, 0x6C, 0x61, 0x6E, 0x64, 0x73, 0x2C, 0x20, 0x6C, 0x6F, 0x6F, 0x74, 0x69, 0x6E, 0x67, 0x20,
                0x73, 0x63, 0x72, 0x61, 0x70, 0x79, 0x61, 0x72, 0x64, 0x73, 0x2C, 0x20, 0x72, 0x61, 0x69, 0x64,
                0x69, 0x6E, 0x67, 0x20, 0x66, 0x75, 0x65, 0x6C, 0x20, 0x64, 0x65, 0x70, 0x6F, 0x74, 0x73, 0x20,
                0xE2, 0x80, 0x93, 0x20, 0x6C, 0x69, 0x66, 0x65, 0x20, 0x6F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20,
                0x72, 0x6F, 0x61, 0x64, 0x20, 0x77, 0x61, 0x73, 0x6E, 0x27, 0x74, 0x20, 0x65, 0x61, 0x73, 0x79,
                0x2E, 0x20, 0x42, 0x75, 0x74, 0x20, 0x67, 0x72, 0x6F, 0x77, 0x69, 0x6E, 0x67, 0x20, 0x75, 0x70,
                0x20, 0x69, 0x6E, 0x20, 0x61, 0x20, 0x6E, 0x6F, 0x6D, 0x61, 0x64, 0x20, 0x63, 0x6C, 0x61, 0x6E,
                0x20, 0x68, 0x61, 0x73, 0x20, 0x69, 0x74, 0x73, 0x20, 0x70, 0x65, 0x72, 0x6B, 0x73, 0x2E, 0x20,
                0x48, 0x6F, 0x6E, 0x65, 0x73, 0x74, 0x79, 0x2C, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x67, 0x72, 0x69,
                0x74, 0x79, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x61, 0x20, 0x6C, 0x6F, 0x76, 0x65, 0x20, 0x6F,
                0x66, 0x20, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6F, 0x6D, 0x20, 0xE2, 0x80, 0x93, 0x20, 0x71, 0x75,
                0x61, 0x6C, 0x69, 0x74, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x66, 0x65, 0x77,
                0x20, 0x69, 0x6E, 0x20, 0x4E, 0x69, 0x67, 0x68, 0x74, 0x20, 0x43, 0x69, 0x74, 0x79, 0x20, 0x70,
                0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, 0x2C, 0x20, 0x61, 0x6E, 0x64, 0x20, 0x6E, 0x6F, 0x20, 0x61,
                0x6D, 0x6F, 0x75, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, 0x6D, 0x6F, 0x6E, 0x65, 0x79, 0x20, 0x63,
                0x61, 0x6E, 0x20, 0x62, 0x75, 0x79, 0x2E
            },
            DisplayName = "05. From lang_en_text/onscreens.json: The entire nomad life path description"
        )]
        public void Test_ReadLengthPrefixedString(string input, byte[] expected)
        {
            using var stream = s_streamManager.GetStream();
            using var writer = new BinaryWriter(stream);
            writer.WriteLengthPrefixedString(input);

            var actual = stream.ToArray();

            CollectionAssert.AreEqual(expected, actual);
        }


        [TestMethod]
        [DataRow(0, new byte[] { 0x00 }, DisplayName = "00. Zero value")]
        [DataRow(42, new byte[] { 0x2A }, DisplayName = "01. Positive 1-byte value")]
        [DataRow(-42, new byte[] { 0xAA }, DisplayName = "02. Negative 1-byte value")]
        [DataRow(63, new byte[] { 0x3F }, DisplayName = "03. Max positive 1-byte value  (2^6 - 1)")]
        [DataRow(-63, new byte[] { 0xBF }, DisplayName = "04. Min negative 1-byte value -(2^6 - 1)")]
        [DataRow(64, new byte[] { 0x40, 0x01 }, DisplayName = "05. Min 2-byte value (2^6)")]
        [DataRow(8191, new byte[] { 0x7F, 0x7F }, DisplayName = "06. Max positive 2-byte value  (2^13 - 1)")]
        [DataRow(-8191, new byte[] { 0xFF, 0x7F }, DisplayName = "07. Max negative 2-byte value -(2^13 - 1)")]
        [DataRow(8192, new byte[] { 0x40, 0x80, 0x01 }, DisplayName = "08. Min 3-byte value (2^13)")]
        [DataRow(1048575, new byte[] { 0x7F, 0xFF, 0x7F }, DisplayName = "09. Max positive 3-byte value  (2^20 - 1)")]
        [DataRow(-1048575, new byte[] { 0xFF, 0xFF, 0x7F }, DisplayName = "10. Max negative 3-byte value -(2^20 - 1)")]
        [DataRow(1048576, new byte[] { 0x40, 0x80, 0x80, 0x01 }, DisplayName = "11. Min 4-byte value (2^20)")]
        [DataRow(134217727, new byte[] { 0x7F, 0xFF, 0xFF, 0x7F }, DisplayName = "12. Max positive 4-byte value  (2^27 - 1)")]
        [DataRow(-134217727, new byte[] { 0xFF, 0xFF, 0xFF, 0x7F }, DisplayName = "13. Max negative 4-byte value -(2^27 - 1)")]
        [DataRow(134217728, new byte[] { 0x40, 0x80, 0x80, 0x80, 0x01 }, DisplayName = "14. Min 5-byte value (2^27)")]
        [DataRow(2147483647, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0x0F }, DisplayName = "15. Max positive 32-bit value  (2^31 - 1)")]
        [DataRow(-2147483647, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0x0F }, DisplayName = "16. Max negative 32-bit value -(2^31 - 1)")]
        public void Test_WriteVLQInt32(int input, byte[] expected)
        {
            using var stream = s_streamManager.GetStream();
            using var writer = new BinaryWriter(stream);
            writer.WriteVLQInt32(input);

            var actual = stream.ToArray();

            CollectionAssert.AreEqual(expected, actual);
        }


        [TestMethod]
        [DataRow(0u, new byte[] { 0x00 }, DisplayName = "00. Zero value")]
        [DataRow(69u, new byte[] { 0x45 }, DisplayName = "01. Random 1-byte value")]
        [DataRow(127u, new byte[] { 0x7F }, DisplayName = "02. Max 1-byte value (2^7 - 1)")]
        [DataRow(128u, new byte[] { 0x80, 0x01 }, DisplayName = "03. Min 2-byte value (2^7)")]
        [DataRow(16383u, new byte[] { 0xFF, 0x7F }, DisplayName = "04. Max 2-byte value (2^14 - 1)")]
        [DataRow(16384u, new byte[] { 0x80, 0x80, 0x01 }, DisplayName = "05. Min 3-byte value (2^14)")]
        [DataRow(2097151u, new byte[] { 0xFF, 0xFF, 0x7F }, DisplayName = "06. Max 3-byte value (2^21 - 1)")]
        [DataRow(2097152u, new byte[] { 0x80, 0x80, 0x80, 0x01 }, DisplayName = "07. Min 4-byte value (2^21)")]
        [DataRow(268435455u, new byte[] { 0xFF, 0xFF, 0xFF, 0x7F }, DisplayName = "08. Max 4-byte value (2^28 - 1)")]
        [DataRow(268435456u, new byte[] { 0x80, 0x80, 0x80, 0x80, 0x01 }, DisplayName = "09. Min 5-byte value (2^21)")]
        [DataRow(4294967295u, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0x0F }, DisplayName = "10. Max 32-bit value (2^32)")]
        public void Test_WriteVLQUInt32(uint input, byte[] expected)
        {
            using var stream = s_streamManager.GetStream();
            using var writer = new BinaryWriter(stream);
            writer.WriteVLQUInt32(input);

            var actual = stream.ToArray();

            CollectionAssert.AreEqual(expected, actual);
        }

        #endregion
    }
}
